package com.voiceqsologger.service;

import com.voiceqsologger.entity.QsoLog;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 参考 CommEntityExtractor 的解析实现，将文本解析为 QSO 关键信息。
 * 仅输出我们系统的字段：callsign/signalReport/rig/antenna/location/rawText。
 */
@Component
public class CommEntityExtractorParser {

    private static final Pattern CALLSIGN_PATTERN = Pattern.compile("\\b([A-Z]{1,2}[0-9][A-Z0-9]{2,4})\\b", Pattern.CASE_INSENSITIVE);
    private static final Pattern CN_CALLSIGN_PATTERN = Pattern.compile("\\b((?:BA|BD|BG|BH|BI)[0-9][A-Z]{2,4})\\b", Pattern.CASE_INSENSITIVE);
    private static final Pattern RST_PATTERN = Pattern.compile("\\b(59|579|599|[1-5][1-9][1-9])\\b");
    private static final Pattern QTH_PATTERN = Pattern.compile("(?:(?:QTH|地址|位置|坐标|在)[:：\\u3001]?)([\\u4e00-\\u9fa5A-Za-z0-9\\-\\s]{2,20})");
    private static final Pattern DEVICE_PATTERN = Pattern.compile("(?:(?:设备|电台|主机|型号|机型)[:：\\u3001]?)([A-Za-z0-9\\-]{2,20})");
    private static final Pattern ANTENNA_PATTERN = Pattern.compile("(?:(?:天线|天綫|天綫类型|天线类型)[:：\\u3001]?)([\\u4e00-\\u9fa5A-Za-z0-9\\-\\s]{2,30})");

    private static final Map<String, String> NATO_MAP = buildNatoMap();
    private static final Set<String> KNOWN_PREFIX = new HashSet<>(Arrays.asList("BA","BD","BG","BH","BI"));

    public QsoLog parse(String text, String myCallsign) {
        if (text == null) text = "";
        String normalized = text.trim();
        String upper = normalized.toUpperCase(Locale.ROOT);

        QsoLog out = new QsoLog();
        out.setRawText(text);
        out.setCreatedAt(LocalDateTime.now());

        findBestOtherCallsign(upper, myCallsign).ifPresent(cs -> out.setCallsign(cs));

        Matcher mR = RST_PATTERN.matcher(upper);
        if (mR.find()) out.setSignalReport(mR.group(1));

        Matcher mQ = QTH_PATTERN.matcher(normalized);
        if (mQ.find()) out.setLocation(clean(mQ.group(1)));

        Matcher mD = DEVICE_PATTERN.matcher(normalized);
        if (mD.find()) out.setRig(mD.group(1).toUpperCase(Locale.ROOT));

        Matcher mA = ANTENNA_PATTERN.matcher(normalized);
        if (mA.find()) out.setAntenna(clean(mA.group(1)));

        return out;
    }

    private Optional<String> findBestOtherCallsign(String upper, String myCallsign) {
        String myCs = myCallsign != null ? myCallsign.toUpperCase(Locale.ROOT) : "";
        List<String> candidates = new ArrayList<>();

        Matcher mCN = CN_CALLSIGN_PATTERN.matcher(upper);
        while (mCN.find()) candidates.add(mCN.group(1));

        String natoCollapsed = collapseSpelledLetters(upper);
        Matcher mCN2 = CN_CALLSIGN_PATTERN.matcher(natoCollapsed);
        while (mCN2.find()) candidates.add(mCN2.group(1));

        Matcher mAny = CALLSIGN_PATTERN.matcher(natoCollapsed);
        while (mAny.find()) candidates.add(mAny.group(1));

        candidates.addAll(collectCollapsedTokenSequences(natoCollapsed));

        String best = null;
        int bestScore = Integer.MIN_VALUE;
        for (String cand : candidates) {
            String normalized = normalizeHomoglyphs(cand);
            if (normalized.equals(myCs)) continue;
            int score = scoreCallsign(normalized);
            if (score > bestScore) { bestScore = score; best = normalized; }
        }
        return Optional.ofNullable(best);
    }

    private static String clean(String s) {
        if (s == null) return null;
        return s.replaceAll("[，。,.]$", "").trim();
    }

    private static Map<String, String> buildNatoMap() {
        Map<String, String> map = new HashMap<>();
        map.put("ALFA", "A"); map.put("ALPHA", "A");
        map.put("BRAVO", "B");
        map.put("CHARLIE", "C");
        map.put("DELTA", "D");
        map.put("ECHO", "E");
        map.put("FOXTROT", "F");
        map.put("GOLF", "G");
        map.put("HOTEL", "H");
        map.put("INDIA", "I");
        map.put("JULIET", "J"); map.put("JULIETT", "J");
        map.put("KILO", "K");
        map.put("LIMA", "L");
        map.put("MIKE", "M");
        map.put("NOVEMBER", "N");
        map.put("OSCAR", "O");
        map.put("PAPA", "P");
        map.put("QUEBEC", "Q");
        map.put("ROMEO", "R");
        map.put("SIERRA", "S");
        map.put("TANGO", "T");
        map.put("UNIFORM", "U");
        map.put("VICTOR", "V");
        map.put("WHISKEY", "W");
        map.put("XRAY", "X"); map.put("X-RAY", "X");
        map.put("YANKEE", "Y");
        map.put("ZULU", "Z");
        map.put("ZERO", "0"); map.put("NIL", "0");
        map.put("ONE", "1"); map.put("TWO", "2"); map.put("THREE", "3"); map.put("FOUR", "4");
        map.put("FIVE", "5"); map.put("SIX", "6"); map.put("SEVEN", "7"); map.put("EIGHT", "8"); map.put("NINE", "9");
        return map;
    }

    private static String collapseSpelledLetters(String textUpper) {
        String replaced = textUpper;
        for (Map.Entry<String, String> e : NATO_MAP.entrySet()) {
            replaced = replaced.replaceAll("\\b" + Pattern.quote(e.getKey()) + "\\b", e.getValue());
        }
        return replaced;
    }

    private static String normalizeHomoglyphs(String s) {
        String u = s.toUpperCase(Locale.ROOT);
        if (u.length() >= 2 && Character.isDigit(u.charAt(1))) {
            u = u.replaceFirst("^B0", "BO");
        }
        u = u.replaceAll("^B8", "BB");
        return u;
    }

    private static int scoreCallsign(String cs) {
        int score = 0;
        String u = cs.toUpperCase(Locale.ROOT);
        int len = u.length();
        if (len >= 4 && len <= 6) score += 5; else if (len <= 7) score += 2; else score -= 2;
        if (u.matches(".*[0-9].*")) score += 3; else score -= 5;
        for (String p : KNOWN_PREFIX) { if (u.startsWith(p)) { score += 6; break; } }
        int digits = u.replaceAll("[^0-9]", "").length();
        if (digits == 1) score += 3; else if (digits > 2) score -= 2;
        if (u.matches("[A-Z0-9]+")) score += 1; else score -= 1;
        return score;
    }

    private static List<String> collectCollapsedTokenSequences(String textUpper) {
        List<String> outs = new ArrayList<>();
        String[] tokens = textUpper.split("\\s+");
        StringBuilder sb = new StringBuilder();
        for (String t : tokens) {
            String tt = t.replaceAll("[^A-Z0-9]", "");
            if (tt.length() == 1) {
                sb.append(tt);
            } else {
                if (sb.length() >= 3) { outs.add(sb.toString()); }
                sb.setLength(0);
                if (tt.length() <= 7) outs.add(tt);
            }
        }
        if (sb.length() >= 3) outs.add(sb.toString());
        List<String> filtered = new ArrayList<>();
        for (String c : outs) {
            if (c.matches("[A-Z]{1,2}[0-9][A-Z0-9]{2,4}")) filtered.add(c);
        }
        return filtered;
    }
}


